package demo.mqserver.core;

import demo.common.MqException;

//实现交换机的转发规则
//验证 bindingKey 是否合法
public class Router {

    //bindingKey 构造规则
    //1.数字，字母，下划线
    //2.使用 . 分割成若干部分
    //3.允许存在 * 和 # 作为通配符，只能作为独立的分段
    public boolean checkBindingKey(String bindingKey){
        if(bindingKey.length() == 0){
            //空字符串也是合法情况，使用 direct 和 fanout 交换机时
            return true;
        }
        //检查字符串中不能存在非法字符串
        for(int i = 0; i < bindingKey.length(); i++){
            char ch = bindingKey.charAt(i);
            if(ch >= 'A' && ch <= 'Z'){
                continue;
            }
            if(ch >= 'a' && ch <= 'z'){
                continue;
            }
            if(ch >= '0' && ch <= '9'){
                continue;
            }
            if(ch == '_' || ch == '.' || ch == '*' || ch == '#'){
                continue;
            }
            return false;
        }
        //检查 * 或者 # 是否是独立的部分
        String[] words = bindingKey.split("\\.");
        for(String word : words){
            // word 的长度 > 1，并且包含 * 或者 #，就是非法的格式
            if(word.length() > 1 && (word.contains("*") || word.contains("#"))){
                return false;
            }
        }
        //约定通配符的相邻关系
        // .#.#. 非法
        // .#.*. 非法
        // .*.#. 非法
        // .*.*. 合法
        for(int i = 0; i < words.length - 1; i++){
            //连续两个 ##
            if(words[i].equals("#") && words[i + 1].equals("#")){
                return false;
            }
            //# 连着 *
            if(words[i].equals("#") && words[i + 1].equals("*")){
                return false;
            }
            //* 连着 #
            if(words[i].equals("*") && words[i + 1].equals("#")){
                return false;
            }
        }
        return true;
    }

    //routingKey 构造规则
    //1.数字，字母，下划线
    //2.使用 . 做分隔符
    public boolean checkRoutingKey(String routingKey){
        if(routingKey.length() == 0){
            //空字符串也是合法情况，使用 fanout 交换机时，routingKey 设置为 “”
            return true;
        }
        for(int i = 0; i < routingKey.length(); i++){
            char ch = routingKey.charAt(i);
            if(ch >= 'A' && ch <= 'Z'){
                continue;
            }
            if(ch >= 'a' && ch <= 'z'){
                continue;
            }
            if(ch >= '0' && ch <= '9'){
                continue;
            }
            if(ch == '_' || ch == '.'){
                continue;
            }
            return false;
        }
        return true;
    }

    //判定该消息是否可以转发给这个绑定对应的队列
    public boolean route(ExchangeType exchangeType, Binding binding, Message message) throws MqException {
        // 根据不同的 exchangeType 使用不同的判定转发规则
        if(exchangeType == ExchangeType.FANOUT){
            //该交换机上的绑定的所有队列都需要转发
            return true;
        }else if(exchangeType == ExchangeType.TOPIC){
            return routeTopic(binding, message);
        }else{
            //其他情况不应该存在
            throw new MqException("[Router] 交换机类型非法！exchangeType = " + exchangeType);
        }
    }

    private boolean routeTopic(Binding binding, Message message) {
        //把两个 key 进行切分
        String[] bindingTokens = binding.getBindingKey().split("\\.");
        String[] routingTokens = message.getRoutingKey().split("\\.");
        //引入两个下标，指向上述的两个数组，初始情况下都为 0
        int bindingIndex = 0;
        int routingIndex = 0;
        while(bindingIndex < bindingTokens.length && routingIndex < routingTokens.length){
            if(bindingTokens[bindingIndex].equals("*")){
                //遇到 *，直接进入下一轮
                bindingIndex++;
                routingIndex++;
                continue;
            }else if(bindingTokens[bindingIndex].equals("#")){
                //遇到 #，需要校验下一个位置
                bindingIndex++;
                if(bindingIndex == bindingTokens.length){
                    //# 后没有值，一定能匹配成功
                    return true;
                }
                //# 后面有值，拿着这个内容去 routingKey 中往后找，找到对应的位置
                //findNextMatch 查找该部分在 routingKey 的位置，返回该下标，没找到就返回 -1
                routingIndex = findNextMatch(routingTokens, routingIndex, bindingTokens[bindingIndex]);
                if(routingIndex == -1){
                    //没找到匹配结果，匹配失败
                    return false;
                }
                //找到匹配的结果，继续往后匹配
                bindingIndex++;
                routingIndex++;
            }else{
                //如果是普通字符串，要求两边的内容是一样的
                if(!bindingTokens[bindingIndex].equals(routingTokens[routingIndex])){
                    return false;
                }
                bindingIndex++;
                routingIndex++;
            }
        }
        //判定双方是否同时到达末尾
        if(bindingIndex == bindingTokens.length && routingIndex == routingTokens.length){
            return true;
        }
        return false;
    }

    private int findNextMatch(String[] routingTokens, int routingIndex, String bindingToken) {
        for(int i = routingIndex; i < routingTokens.length; i++){
            if(routingTokens[i].equals(bindingToken)){
                return i;
            }
        }
        return -1;
    }
}
